package org.springblade.develop.support;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.medusa.gruul.common.core.util.Func;
import com.medusa.gruul.common.core.util.StringUtil;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Mapper;
import org.springblade.develop.vo.SuperClassEnum;
import org.springblade.develop.vo.TemplateServiceStatus;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 代码生成器配置类
 *
 * @author qinlei
 */
@Data
@Slf4j
public class ByCodeGenerator {

	/**
	 * api代码生成的包名
	 */

	@Setter(AccessLevel.NONE)
	private String packageName;
	/**
	 * 作者信息
	 */
	private String author;
	/**
	 * api代码后端生成的地址
	 */
	private String apiPackageDir;
	/**
	 * server代码后端生成的地址
	 */
	private String serverPackageDir;
	/**
	 * 需要生成的表名
	 */
	private String includeTable;
	/**
	 * 继承资源
	 *
	 * @author: QinLei
	 * @date: 2023年08月10日  9:21
	 */
	private SuperClassEnum superClassEnum = SuperClassEnum.BASE_ENTITY;

	/**
	 * 模板服务选择
	 *
	 * @author: QinLei
	 * @date: 2023年08月11日  8:59
	 */
	private TemplateServiceStatus templateServiceStatus = new TemplateServiceStatus(false, false, false);

	/**
	 * 数据库链接地址
	 */
	private String url;
	/**
	 * 数据库用户名
	 */
	private String username;
	/**
	 * 数据库密码
	 */
	private String password;
	/**
	 * schema
	 */
	private String schema = "public";

	/**
	 * 代码生成执行
	 */
	public void run() {
		getAutoGenerator(getCustomMap(), getCustomFile()).templateEngine(new ByTemplateEngine(getApiOutputDir(), getServerOutputDir())).execute();
	}

	/**
	 * 设置 customMap
	 */
	private Map<String, Object> getCustomMap() {
		Map<String, Object> customMap = new HashMap<>(4);
		customMap.put("packageName", packageName);
		customMap.put("templateServiceStatus", templateServiceStatus);
		String entityName = StringUtil.underlineToHump(this.includeTable);
		customMap.put("entityCode", entityName);
		customMap.put("entityName", StringUtil.firstCharToUpper(entityName));
		return customMap;
	}

	/**
	 * 设置 customFile
	 */
	private Map<String, String> getCustomFile() {
		Map<String, String> customFile = new HashMap<>();
		customFile.put("entityVO.java", "/templates/java/entityVO.java.btl");
		customFile.put("entityParam.java", "/templates/java/entityParam.java.btl");
		customFile.put("controller.java", "/templates/java/controller.java.btl");
		customFile.put("entity.java", "/templates/java/entity.java.btl");
		customFile.put("mapper.java", "/templates/java/mapper.java.btl");
		customFile.put("mapper.xml", "/templates/java/mapper.xml.btl");
		customFile.put("service.java", "/templates/java/service.java.btl");
		customFile.put("serviceImpl.java", "/templates/java/serviceImpl.java.btl");
		if (templateServiceStatus.getHasWrapper()) {
			customFile.put("wrapper.java", "/templates/java/wrapper.java.btl");
		}
		if (templateServiceStatus.getHasExcel()) {
			customFile.put("entityExcel.java", "/templates/java/entityExcel.java.btl");
		}
		customFile.put("entityDTO.java", "/templates/java/entityDTO.java.btl");
		if (templateServiceStatus.getHasFeign()) {
			customFile.put("feign.java", "/templates/java/feign.java.btl");
			customFile.put("feignclient.java", "/templates/java/feignclient.java.btl");
			customFile.put("feignclientFallback.java", "/templates/java/feignclientFallback.java.btl");
		}
		return customFile;
	}

	private FastAutoGenerator getAutoGenerator(Map<String, Object> customMap, Map<String, String> customFile) {
		Properties props = getProperties();
		String dbUrl = Func.toStr(this.url, props.getProperty("spring.datasource.url"));
		String uname = Func.toStr(this.username, props.getProperty("spring.datasource.username"));
		String pwd = Func.toStr(this.password, props.getProperty("spring.datasource.password"));
		FastAutoGenerator generator = FastAutoGenerator.create(dbUrl, uname, pwd);
		generator.dataSourceConfig(builder -> dataSourceConfigGenerator(builder));
		// 全局配置
		generator.globalConfig(builder -> globalConfigGenerator(builder, props));
		// 包配置
		generator.packageConfig(builder -> packageConfigGenerator(builder));
		// 策略配置
		generator.strategyConfig(builder -> strategyConfigGenerator(builder));
		// 注入配置
		generator.injectionConfig(builder -> injectionConfigGenerator(builder, customMap, customFile));
		// 模板配置
		generator.templateConfig(builder -> templateConfigGenerator(builder));
		return generator;
	}

	private void dataSourceConfigGenerator(DataSourceConfig.Builder builder) {
		builder.schema(this.schema)
			// 解决类型为int2时 输出short的问题
			.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
				if (metaInfo.getJdbcType().name().equals("SMALLINT")) {
					return DbColumnType.INTEGER;
				}
				return typeRegistry.getColumnType(metaInfo);
			});
	}

	/**
	 * entity策略 service策略 controller策略
	 *
	 * @param builder
	 * @author: QinLei
	 * @date: 2023年08月09日  17:23
	 */
	private void strategyConfigGenerator(StrategyConfig.Builder builder) {
		builder.addInclude(this.includeTable);
		// entity
		builder.entityBuilder().naming(NamingStrategy.underline_to_camel)
			.columnNaming(NamingStrategy.underline_to_camel)
			// 设置父类
			.superClass(superClassEnum.getSuperEntityClass()).formatFileName("%sEntity")
			.addSuperEntityColumns(superClassEnum.getSuperEntityColumns())
			.enableFileOverride();
		// service
		builder.serviceBuilder()
			.superServiceClass(superClassEnum.getSuperServiceClass())
			.superServiceImplClass(superClassEnum.getSuperServiceImplClass())
			.formatServiceFileName("I%sService").formatServiceImplFileName("%sServiceImpl")
			.enableFileOverride();
		// mapper
		builder.mapperBuilder().mapperAnnotation(Mapper.class)
			.enableBaseResultMap().enableBaseColumnList().formatMapperFileName("%sMapper")
			.formatXmlFileName("%sMapper").enableFileOverride();
		// controller
		builder.controllerBuilder()
			.superClass(superClassEnum.getSuperControllerClass()).formatFileName("%sController")
			.enableRestStyle().enableHyphenStyle().enableFileOverride();
	}

	/**
	 * 全局配置
	 *
	 * @param builder
	 * @param props
	 * @author: QinLei
	 * @date: 2023年08月09日  17:18
	 */
	private void globalConfigGenerator(GlobalConfig.Builder builder, Properties props) {
		builder.author(StringUtil.isBlank(author) ? props.getProperty("author") : author)
			.dateType(DateType.TIME_PACK)
			.outputDir(getApiOutputDir())
			.enableSwagger().disableOpenDir();
	}

	private String getApiOutputDir() {
		return apiPackageDir + "/src/main/java";
	}

	/**
	 * 包配置
	 *
	 * @param builder
	 * @author: QinLei
	 * @date: 2023年08月09日  17:21
	 */
	private void packageConfigGenerator(PackageConfig.Builder builder) {
		builder.parent(packageName)
//			.pathInfo(setPathInfo())
			.entity("entity")
			.controller("controller")
			.service("service").serviceImpl("service.impl")
			.mapper("mapper").xml("mapper.xml");
	}

	/**
	 * 获取配置文件
	 *
	 * @return 配置Props
	 */
	private Properties getProperties() {
		// 读取配置文件
		Resource resource = new ClassPathResource("/code.properties");
		Properties props = new Properties();
		try {
			props = PropertiesLoaderUtils.loadProperties(resource);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return props;
	}

	/**
	 * 注入策略配置,也就是模板字段替换
	 *
	 * @param builder
	 * @author: QinLei
	 * @date: 2023年08月09日  17:25
	 */
	private void injectionConfigGenerator(InjectionConfig.Builder builder, Map<String, Object> customMap, Map<String, String> customFile) {
		builder.customMap(customMap).customFile(customFile);
	}

	/**
	 * 模板配置
	 *
	 * @param builder
	 * @author: QinLei
	 * @date: 2023年08月09日  17:27
	 */
	private void templateConfigGenerator(TemplateConfig.Builder builder) {
		builder.disable(TemplateType.ENTITY)
			.entity("/templates/java/entity.java")
			.service("/templates/java/service.java")
			.serviceImpl("/templates/java/serviceImpl.java")
			.mapper("/templates/java/mapper.java")
			.xml("/templates/java/mapper.xml")
			.controller("/templates/java/controller.java");
	}

	private String getServerOutputDir() {
		return serverPackageDir + "/src/main/java";
	}

	/**
	 * 动态设置pathinfo
	 *
	 * @return
	 * @author: QinLei
	 * @date: 2023年08月11日  15:09
	 */
	private Map<OutputFile, String> setPathInfo() {
		Map<OutputFile, String> pathInfo = new HashMap<>(6);
		pathInfo.put(OutputFile.entity, getApiOutputDir());
		pathInfo.put(OutputFile.mapper, getServerOutputDir());
		pathInfo.put(OutputFile.xml, getServerOutputDir());
		pathInfo.put(OutputFile.service, getServerOutputDir());
		pathInfo.put(OutputFile.serviceImpl, getServerOutputDir());
		pathInfo.put(OutputFile.controller, getServerOutputDir());
		return pathInfo;
	}

	public void setPackageName(String packageName) {
		this.packageName = "org.springblade." + packageName;
	}

}
